package com.jintu.portal.jpa;

import com.jintu.portal.pojo.bean.BaseEntity;
import com.jintu.portal.pojo.bean.QLBuilder;
import com.jintu.portal.utils.BeanUtils;
import com.jintu.portal.utils.StringUtil;
import org.hibernate.SQLQuery;
import org.hibernate.transform.Transformers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.*;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BaseDaoImpl<T extends BaseEntity, ID extends Serializable> implements BaseDao<T, ID> {

	private static final Logger logger = LoggerFactory.getLogger(BaseDaoImpl.class);

	private final static Set<Class> baseClass = new HashSet<Class>() {
		{
			add(Integer.class);
			add(BigInteger.class);
			add(Double.class);
			add(BigDecimal.class);
			add(Long.class);
			add(String.class);
			add(Date.class);
		}
	};

	/**
	 * entityClass 实体类型
	 */
	private Class<?> entityClass;

	@PersistenceContext
	private EntityManager entityManager;

	/**
	 * 默认构造函数
	 */
	public BaseDaoImpl() {
		Type type = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		if(!type.toString().equals("T")) {
			this.entityClass = (Class<T>)type;
		}
	}

	/**
	 * 构造函数
	 * @param entityClass 当前实体类型
	 */
	public BaseDaoImpl(Class<?> entityClass)
	{
		this.entityClass = entityClass;
	}


	public EntityManager getEntityManager()
	{
		return this.entityManager;
	}

	/* (non-Javadoc)
     * @see com.gxzy.framework.util.persistence.GenericDao#insert(java.lang.Object)
     */
	@Transactional
	public void insert(T t) {
		// TODO Auto-generated method stub
		this.getEntityManager().persist(t);
	}

	/* (non-Javadoc)
     * @see com.gxzy.framework.util.persistence.GenericDao#update(java.lang.Object)
     */
	public void update(T t) {
		// TODO Auto-generated method stub
		this.getEntityManager().merge(t);
	}
//
//	public void delete(ID id) {
//		Object o = this.getEntityManager().find(this.entityClass, id);
//		if(o == null){
//			return;
//		}
//		this.getEntityManager().remove(o);
//	}

	public T get(ID id) {
		return (T)this.getEntityManager().find(this.entityClass, id);
	}
//
//	public int executeBySql(String sql, Object... params) {
//		return createNativeQuery(sql, Arrays.asList(params)).executeUpdate();
//	}
//
//	public int executeByJpql(String jpql, Object... params) {
//		return createQuery(jpql, Arrays.asList(params)).executeUpdate();
//	}
//
//	public int deleteByJpql(QLBuilder ql) {
//		if(StringUtil.isBlank(ql.getMainClause())) {
//			ql.setMainClause("DELETE FROM " + this.entityClass.getSimpleName() + " AS t");
//		}
//		return createQuery(ql.getFullQL(), ql.getParams()).executeUpdate();
//	}

	public <T> T findOneByJpql(QLBuilder ql) {
		try {
			if(StringUtil.isBlank(ql.getMainClause())) {
				ql.setMainClause("SELECT t FROM " + this.entityClass.getSimpleName() + " AS t");
			}
			return (T)createQuery(ql.getFullQL(), ql.getParams()).getSingleResult();
		}catch(NoResultException e) {
			return null;
		}
	}

//	public <T> T findOneByJpql(String jpql, Object... params) {
//		try {
//			return (T)createQuery(jpql, Arrays.asList(params)).getSingleResult();
//		}catch(NoResultException e) {
//			return null;
//		}
//	}
//
//	public <T> T findOneBySql(String sql, Class<T> clz, Object... params) {
//		Map<String, Object> map = null;
//		try {
//			Query query = createNativeQuery(sql, Arrays.asList(params));
//			query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
//			map = (Map<String, Object>)query.getSingleResult();
//		}catch(NoResultException e) {
//			return null;
//		}
//		if(baseClass.contains(clz)) {
//			for (String key : map.keySet()) {
//				return (T)map.get(key);
//			}
//		}
//		try {
//			return BeanUtils.convertMapToBean(map, clz);
//		} catch (Exception e) {
//			logger.error(e.getMessage(), e);
//			return null;
//		}
//	}
//
	public <T> List<T> findManyByJpql(QLBuilder ql) {
		if(StringUtil.isBlank(ql.getMainClause())) {
			ql.setMainClause("SELECT t FROM " + this.entityClass.getSimpleName() + " AS t");
		}
		return createQuery(ql.getFullQL(), ql.getParams()).getResultList();
	}
//
//	public <T> List<T> findManyByJpql(String jpql, Object... params) {
//		return createQuery(jpql, Arrays.asList(params)).getResultList();
//	}
//
//	public List findBySql(String sql){
//		Query query = this.getEntityManager().createNativeQuery(sql);
//		List<Map<String, Object>> mapList = query.getResultList();
//		return mapList;
//	}
//
	public <T> List<T> findManyBySql(String sql, Class<T> clz, Object... params) {
		Query query = createNativeQuery(sql, Arrays.asList(params));
		query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		List<Map<String, Object>> mapList = query.getResultList();
		if(baseClass.contains(clz)) {
			List<T> list = new ArrayList<T>();
			for (Map<String, Object> map : mapList) {
				for (String key : map.keySet()) {
					list.add((T)map.get(key));
					continue;
				}
			}
			return list;
		}
		try {
			return BeanUtils.convertMapToBean(mapList, clz);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			return null;
		}
	}
//
//	public <T> List<T> findLimitByJpql(QLBuilder ql, Integer limit) {
//		if(StringUtil.isBlank(ql.getMainClause())) {
//			ql.setMainClause("SELECT t FROM " + this.entityClass.getSimpleName() + " AS t");
//		}
//		return createQuery(ql.getFullQL(), ql.getParams()).setFirstResult(0).setMaxResults(limit).getResultList();
//	}
//
//	public <T> List<T> findLimitByJpql(String jpql, Integer limit, Object... params) {
//		return createQuery(jpql, Arrays.asList(params)).setFirstResult(0).setMaxResults(limit).getResultList();
//	}
//
//	public <T> List<T> findLimitBySql(String sql, Class<T> clz, Integer limit, Object... params) {
//		Query query = createNativeQuery(sql, Arrays.asList(params));
//		query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
//		List<Map<String, Object>> mapList = query.setFirstResult(0).setMaxResults(limit).getResultList();
//		if(baseClass.contains(clz)) {
//			List<T> list = new ArrayList<T>();
//			for (Map<String, Object> map : mapList) {
//				for (String key : map.keySet()) {
//					list.add((T)map.get(key));
//					continue;
//				}
//			}
//			return list;
//		}
//		try {
//			return BeanUtils.convertMapToBean(mapList, clz);
//		} catch (Exception e) {
//			logger.error(e.getMessage(), e);
//			return null;
//		}
//	}
//
//	public <T> Pagination<T> findPageByJpql(QLBuilder ql, Pageable pageable) {
//		if(StringUtil.isBlank(ql.getMainClause())) {
//			ql.setMainClause("SELECT t FROM " + this.entityClass.getSimpleName() + " AS t");
//		}
//		Pagination<T> page = new Pagination<T>();
//		page.setPage(pageable.getPageNumber());
//		page.setLimit(pageable.getPageSize());
//		Query query = this.createQuery(ql.getFullQL(), ql.getParams());
//		Query countQuery = this.createQuery(this.getCountQL(ql.getFullQL()), ql.getParams());
//
//		if(pageable.getPageSize() != Pageable.NO_PAGE) {
//			query.setFirstResult(page.getStart());
//			query.setMaxResults(page.getLimit());
//		}
//
//		page.setItems(query.getResultList());
//		page.setTotal( Long.parseLong(countQuery.getSingleResult().toString()));
//		return page;
//	}
//
//	public <T> Pagination<T> findPageByJpql(String jpql, Pageable pageable, Object... params) {
//		Pagination<T> page = new Pagination<T>();
//		page.setPage(pageable.getPageNumber());
//		page.setLimit(pageable.getPageSize());
//		Query query = this.createQuery(jpql, Arrays.asList(params));
//		Query countQuery = this.createQuery(this.getCountQL(jpql), Arrays.asList(params));
//
//		if(pageable.getPageSize() != Pageable.NO_PAGE) {
//			query.setFirstResult(page.getStart());
//			query.setMaxResults(page.getLimit());
//		}
//
//		page.setItems(query.getResultList());
//		page.setTotal( Long.parseLong(countQuery.getSingleResult().toString()));
//		return page;
//	}
//
//	public <T> Pagination<T> findPageBySql(String sql, Pageable pageable, Class<T> clz, Object... params) {
//		Pagination<T> page = new Pagination<T>();
//		page.setPage(pageable.getPageNumber());
//		page.setLimit(pageable.getPageSize());
//		Query query = this.createNativeQuery(sql, Arrays.asList(params));
//		query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
//		Query countQuery = this.createNativeQuery(this.getCountSql(sql), Arrays.asList(params));
//
//		if(pageable.getPageSize() != Pageable.NO_PAGE) {
//			query.setFirstResult(page.getStart());
//			query.setMaxResults(page.getLimit());
//		}
//
//		List<Map<String, Object>> mapList = query.getResultList();
//		if(baseClass.contains(clz)) {
//			List<T> list = new ArrayList<T>();
//			for (Map<String, Object> map : mapList) {
//				for (String key : map.keySet()) {
//					list.add((T)map.get(key));
//					continue;
//				}
//			}
//			page.setItems(list);
//			return page;
//		}
//
//		try {
//			page.setItems(BeanUtils.convertMapToBean(mapList, clz));
//		} catch (Exception e) {
//			logger.error(e.getMessage(), e);
//		}
//		page.setTotal( Long.parseLong(countQuery.getSingleResult().toString()));
//		return page;
//	}
//
////	public JPASQLQuery jpaSqlQuery() {
////		SQLTemplates dialect = new HSQLDBTemplates();
////		JPASQLQuery jpasqlQuery = new JPASQLQuery(this.getEntityManager(), dialect);
////		return jpasqlQuery;
////	}
//
//	private String getCountSql(String ql) {
//		StringBuffer countJpql = new StringBuffer("SELECT count(1) FROM (SELECT 1 ").append(ql.substring(ql.lastIndexOf("FROM"))).append(") t");
//		return countJpql.toString();
//	}
//
//	private String getCountQL(String ql) {
//		StringBuffer countJpql = new StringBuffer("SELECT count(1) ").append(ql.substring(ql.lastIndexOf("FROM")));
//		return countJpql.toString();
//	}
//
	private Query createQuery(String jpql, List<Object> params) {
		Query query = this.getEntityManager().createQuery(jpql);
		String[] paramNames = this.resolveVarlible(jpql);
		this.setQueryParameterValues(query, paramNames, params);
		return query;
	}
//
	private Query createNativeQuery(String sql, List<Object> params) {
		Query query = this.getEntityManager().createNativeQuery(sql);
		String[] paramNames = this.resolveVarlible(sql);
		this.setQueryParameterValues(query, paramNames, params);
		return query;
	}
//
	private void setQueryParameterValues(Query query, String[] paramNames, List<Object> params) {
		for (int i = 0; i < params.size(); i++) {
			query.setParameter(paramNames[i], params.get(i));
		}
	}

	/**
	 * 获取查询语句中的所有变量名，并以数组形式返回
	 *
	 * @param ql 查询语句，可以是jpql或者是sql
	 * @return 参数名列表，如果没有参数返回为null
	 */
	private String[] resolveVarlible(String ql) {
		List<String> list = new ArrayList<String>();
		String regex = "\\:\\w+";

		Pattern p = Pattern.compile(regex);
		Matcher m = p.matcher(ql);

		String val = null;

		while (m.find()) {
			val = m.group();
			list.add(val);
		}

		if (val == null) {
			return null;
		}else {
			String[] result = new String[list.size()];
			for(int i=0; i<result.length; i++){
				result[i] = list.get(i).substring(1);
			}
			return result;
		}
	}

	private String getEntityTableName() {
		Table table = this.entityClass.getAnnotation(Table.class);
		if(table!=null){
			return table.name();
		}
		return this.entityClass.getSimpleName();
	}

	protected void setEntityClass(Class<?> entityClass) {
		this.entityClass = entityClass;
	}

	protected Class<?> getEntityClass() {
		return this.entityClass;
	}
}
